﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// 并行扩展类
    /// </summary>
    public class ParallelEx
    {
        /// <summary>
        /// 并行执行,当不能整除的情况下,余数会分散到前余数个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static void ParallelExcute<TSource>(TSource source, int startIndex, int count, int threadCount, object tag, Action<ParalleActionItem<TSource>> action)
        {
            int segLength = count / threadCount;
            int mod = count % threadCount;
            var parallelItems = new ParalleActionItem<TSource>[threadCount];
            int beginIndex = startIndex, endIndex;
            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (mod > 0)
                {
                    endIndex += 1;
                    mod -= 1;
                }

                parallelItems[i] = new ParalleActionItem<TSource>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数会分散到前余数个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <typeparam name="TResult">每块数据计算结果类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static ParalleFuncItem<TSource, TResult>[] ParallelExcute<TSource, TResult>(TSource source, int startIndex, int count, int threadCount, object tag, Action<ParalleFuncItem<TSource, TResult>> action)
        {
            int segLength = count / threadCount;
            int mod = count % threadCount;
            var parallelItems = new ParalleFuncItem<TSource, TResult>[threadCount];
            int beginIndex = startIndex, endIndex;
            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (mod > 0)
                {
                    endIndex += 1;
                    mod -= 1;
                }

                parallelItems[i] = new ParalleFuncItem<TSource, TResult>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
            return parallelItems;
        }









        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static void ParallelExcuteNoMod<TSource>(TSource source, int startIndex, int count, int threadCount, object tag, Action<ParalleActionItem<TSource>> action)
        {
            int segLength = count / threadCount;
            var parallelItems = new ParalleActionItem<TSource>[threadCount];
            int lastThredaIndex = threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                parallelItems[i] = new ParalleActionItem<TSource>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="segFactor">段系数，除最后一段外，每段数据长度都是该公值的整数倍数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static void ParallelExcuteNoMod<TSource>(TSource source, int startIndex, int count, int threadCount, int segFactor, object tag, Action<ParalleActionItem<TSource>> action)
        {
            int segLength = count / threadCount;
            int segMod = segLength % segFactor;
            if (segMod != 0)
            {
                segLength += (segFactor - segMod);
            }
            var parallelItems = new ParalleActionItem<TSource>[threadCount];
            int lastThredaIndex = threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                parallelItems[i] = new ParalleActionItem<TSource>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
        }






        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <typeparam name="TResult">每块数据计算结果类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static ParalleFuncItem<TSource, TResult>[] ParallelExcuteNoMod<TSource, TResult>(TSource source, int startIndex, int count, int threadCount, object tag, Action<ParalleFuncItem<TSource, TResult>> action)
        {
            int segLength = count / threadCount;
            var parallelItems = new ParalleFuncItem<TSource, TResult>[threadCount];
            int lastThredaIndex = threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                parallelItems[i] = new ParalleFuncItem<TSource, TResult>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
            return parallelItems;
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
        /// <typeparam name="TResult">每块数据计算结果类型</typeparam>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="segFactor">段系数，除最后一段外，每段数据长度都是该公值的整数倍数</param>
        /// <param name="tag">object附加数据对象</param>
        /// <param name="action">执行并行操作的委托</param>
        public static ParalleFuncItem<TSource, TResult>[] ParallelExcuteNoMod<TSource, TResult>(TSource source, int startIndex, int count, int segFactor, int threadCount, object tag, Action<ParalleFuncItem<TSource, TResult>> action)
        {
            int segLength = count / threadCount;
            int segMod = segLength % segFactor;
            if (segMod != 0)
            {
                segLength += (segFactor - segMod);
            }
            var parallelItems = new ParalleFuncItem<TSource, TResult>[threadCount];
            int lastThredaIndex = threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                parallelItems[i] = new ParalleFuncItem<TSource, TResult>(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(parallelItems, action);
            return parallelItems;
        }

    }



    /// <summary>
    /// 并行扩展类
    /// </summary>
    /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
    public class ParalleAction<TSource>
    {
        private readonly int _threadCount;
        private readonly ParalleActionItem<TSource>[] _parallelItems;
        private readonly Action<ParalleActionItem<TSource>> _action;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="action">执行并行操作的委托</param>
        public ParalleAction(int threadCount, Action<ParalleActionItem<TSource>> action)
        {
            this._threadCount = threadCount;

            var parallelItems = new ParalleActionItem<TSource>[threadCount];
            for (int i = 0; i < threadCount; i++)
            {
                parallelItems[i] = new ParalleActionItem<TSource>();
            }
            this._parallelItems = parallelItems;
            this._action = action;
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数会分散到前余数个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="tag">object附加数据对象</param>
        public void ParallelExcute(TSource source, int startIndex, int count, object tag)
        {
            int segLength = count / this._threadCount;
            int mod = count % this._threadCount;
            int beginIndex = startIndex, endIndex;
            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (mod > 0)
                {
                    endIndex += 1;
                    mod -= 1;
                }

                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="tag">object附加数据对象</param>
        public void ParallelExcuteNoMod(TSource source, int startIndex, int count, object tag)
        {
            int segLength = count / this._threadCount;
            int lastThredaIndex = this._threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="segFactor">段系数，除最后一段外，每段数据长度都是该公值的整数倍数</param>
        /// <param name="tag">object附加数据对象</param>
        public void ParallelExcuteNoMod(TSource source, int startIndex, int count, int segFactor, object tag)
        {
            int segLength = count / this._threadCount;
            int segMod = segLength % segFactor;
            if (segMod != 0)
            {
                segLength += (segFactor - segMod);
            }
            int lastThredaIndex = this._threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
        }

    }



    /// <summary>
    /// 并行扩展类,带返回值
    /// </summary>
    /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
    /// <typeparam name="TResult">每块数据计算结果类型</typeparam>
    public class ParalleFunc<TSource, TResult>
    {
        private readonly int _threadCount;
        private readonly ParalleFuncItem<TSource, TResult>[] _parallelItems;
        private readonly Action<ParalleFuncItem<TSource, TResult>> _action;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="threadCount">用于并行处理的线程数</param>
        /// <param name="action">执行并行操作的委托</param>
        public ParalleFunc(int threadCount, Action<ParalleFuncItem<TSource, TResult>> action)
        {
            this._threadCount = threadCount;
            var parallelItems = new ParalleFuncItem<TSource, TResult>[threadCount];
            for (int i = 0; i < threadCount; i++)
            {
                parallelItems[i] = new ParalleFuncItem<TSource, TResult>();
            }
            this._parallelItems = parallelItems;
            this._action = action;
        }


        /// <summary>
        /// 并行执行,当不能整除的情况下,余数会分散到煎蛋余数个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="tag">object附加数据对象</param>
        public ParalleFuncItem<TSource, TResult>[] Excute(TSource source, int startIndex, int count, object tag)
        {
            int segLength = count / this._threadCount;
            int mod = count % this._threadCount;
            int beginIndex = startIndex, endIndex;
            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (mod > 0)
                {
                    endIndex += 1;
                    mod -= 1;
                }

                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
            return this._parallelItems;
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="tag">object附加数据对象</param>
        public ParalleFuncItem<TSource, TResult>[] ParallelExcuteNoMod(TSource source, int startIndex, int count, object tag)
        {
            int segLength = count / this._threadCount;
            int lastThredaIndex = this._threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
            return this._parallelItems;
        }

        /// <summary>
        /// 并行执行,当不能整除的情况下,余数为单独一个线程
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="segFactor">段系数，除最后一段外，每段数据长度都是该公值的整数倍数</param>
        /// <param name="tag">object附加数据对象</param>
        public ParalleFuncItem<TSource, TResult>[] ParallelExcuteNoMod(TSource source, int startIndex, int count, int segFactor, object tag)
        {
            int segLength = count / this._threadCount;
            int segMod = segLength % segFactor;
            if (segMod != 0)
            {
                segLength += (segFactor - segMod);
            }
            int lastThredaIndex = this._threadCount - 1;
            int beginIndex = startIndex, endIndex;

            for (int i = 0; i < this._threadCount; i++)
            {
                endIndex = beginIndex + segLength;
                if (i == lastThredaIndex)
                {
                    endIndex = count;
                }
                this._parallelItems[i].Set(source, startIndex, count, beginIndex, endIndex, i, tag);
                beginIndex = endIndex;
            }

            Parallel.ForEach(this._parallelItems, this._action);
            return this._parallelItems;
        }


    }





    /// <summary>
    /// 并行执行的项
    /// </summary>
    /// <typeparam name="T">用于并行处理的数据类型</typeparam>
    public class ParalleActionItem<T>
    {
        /// <summary>
        /// 用于并行处理的数据
        /// </summary>
        public T Source { get; private set; }

        /// <summary>
        /// 起始索引
        /// </summary>
        public int StartIndex { get; private set; }

        /// <summary>
        /// 用于并行处理的元素个数
        /// </summary>
        public int Count { get; private set; }





        /// <summary>
        /// 当前线程处理的起始索引,包含
        /// </summary>
        public int BeginIndex { get; private set; }

        /// <summary>
        /// 当前线程处理的结束索引,不包含
        /// </summary>
        public int EndIndex { get; private set; }

        /// <summary>
        /// 数据段标识
        /// </summary>
        public int SegId { get; private set; }

        /// <summary>
        /// object附加数据对象
        /// </summary>
        public object Tag { get; private set; }


        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="beginIndex">当前线程处理的起始索引</param>
        /// <param name="endIndex">当前线程处理的结束索引</param>
        /// <param name="segId">数据段标识ID</param>
        /// <param name="tag">object附加数据对象</param>
        public ParalleActionItem(T source, int startIndex, int count, int beginIndex, int endIndex, int segId, object tag)
        {
            this.Source = source;
            this.StartIndex = startIndex;
            this.Count = count;
            this.BeginIndex = beginIndex;
            this.EndIndex = endIndex;
            this.SegId = segId;
            this.Tag = tag;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ParalleActionItem()
        {

        }

        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="beginIndex">当前线程处理的起始索引</param>
        /// <param name="endIndex">当前线程处理的结束索引</param>
        /// <param name="segId">数据段标识ID</param>
        /// <param name="tag">object附加数据对象</param>
        public void Set(T source, int startIndex, int count, int beginIndex, int endIndex, int segId, object tag)
        {
            this.Source = source;
            this.StartIndex = startIndex;
            this.Count = count;
            this.BeginIndex = beginIndex;
            this.EndIndex = endIndex;
            this.SegId = segId;
            this.Tag = tag;
        }

    }

    /// <summary>
    /// 并行执行的项
    /// </summary>
    /// <typeparam name="TSource">用于并行处理的数据类型</typeparam>
    /// <typeparam name="TResult">每块数据计算结果类型</typeparam>
    public class ParalleFuncItem<TSource, TResult> : ParalleActionItem<TSource>
    {
        /// <summary>
        /// 每块数据计算结果
        /// </summary>
        public TResult Result { get; set; }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="source">用于并行处理的数据</param>
        /// <param name="startIndex">起始索引</param>
        /// <param name="count">用于并行处理的元素个数</param>
        /// <param name="beginIndex">当前线程处理的起始索引</param>
        /// <param name="endIndex">当前线程处理的结束索引</param>
        /// <param name="segId">数据段标识ID</param>
        /// <param name="tag">object附加数据对象</param>
        public ParalleFuncItem(TSource source, int startIndex, int count, int beginIndex, int endIndex, int segId, object tag)
            : base(source, startIndex, count, beginIndex, endIndex, segId, tag)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ParalleFuncItem()
            : base()
        {

        }
    }

}
